package com.bjy.qa.agent.tester;

import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONArray;
import com.alibaba.fastjson.JSONObject;
import com.bjy.qa.agent.enumtype.CatalogType;
import com.bjy.qa.agent.enumtype.RunningModeStatus;
import com.bjy.qa.agent.tester.handler.tester.TesterStepHandler;
import com.bjy.qa.agent.tester.handler.tester.TesterTaskBootThread;
import org.apache.commons.lang.StringUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.testng.ITestContext;
import org.testng.annotations.DataProvider;
import org.testng.annotations.Test;

import java.io.IOException;
import java.util.*;

import static com.bjy.qa.agent.tester.SuiteListener.runningTestsMap;

/**
 * 测试执行器
 */
public class Tester {
    private static final Logger logger = LoggerFactory.getLogger(Tester.class);

    @DataProvider(name = "caseInfo", parallel = false)
    public Object[][] getTestData(ITestContext context) {
        JSONObject dataInfo = JSON.parseObject(context.getCurrentXmlTest().getParameter("casesInfo"));

        // 生成测试数据
        List<JSONObject> dataProvider = new ArrayList<>();
        List<Map<String, String>> testDataList = convertDataProvider(JSON.parseArray(dataInfo.getString("dataProvider")));
        if (testDataList.size() == 0) { // 测试用例没有 DataProvider
            JSONObject caseInfo = new JSONObject();
            caseInfo.put("steps", dataInfo.getJSONArray("steps"));
            caseInfo.put("ctype", CatalogType.INTERFACE_TEST_CASE.getValue());
            caseInfo.put("rid", dataInfo.getInteger("rid"));
            caseInfo.put("cid", dataInfo.getInteger("cid"));
            caseInfo.put("gp", dataInfo.getJSONObject("gp"));
            caseInfo.put("ic", "1-1"); // ic: 迭代次数（iteration count）
            dataProvider.add(caseInfo);
        } else { // 测试用例有 DataProvider
            for (int i = 0; i < testDataList.size(); i++) {
                Map testData = testDataList.get(i);
                JSONObject caseInfo = new JSONObject();
                caseInfo.put("steps", dataInfo.getJSONArray("steps"));
                caseInfo.put("ctype", CatalogType.INTERFACE_TEST_CASE.getValue());
                caseInfo.put("rid", dataInfo.getInteger("rid"));
                caseInfo.put("cid", dataInfo.getInteger("cid"));
                caseInfo.put("gp", dataInfo.getJSONObject("gp"));
                caseInfo.put("ic", (i + 1) + "-" + testDataList.size()); // ic: 迭代次数（iteration count）
                caseInfo.put("testData", testData);
                dataProvider.add(caseInfo);
            }
        }

        Object[][] testDataProvider = new Object[dataProvider.size()][];
        for (int i = 0; i < dataProvider.size(); i++) {
            testDataProvider[i] = new Object[]{dataProvider.get(i)};
        }

        return testDataProvider;
    }

    @Test(dataProvider = "caseInfo")
    public void run(JSONObject jsonObject) throws IOException {
        logger.info("运行用例：{}", jsonObject);

        // 重复任务，忽略
        int rid = jsonObject.getInteger("rid"); // result id
        if (TaskManager.ridRunning(rid)) {
            logger.info("忽略重复（可能是网络原因导致）忽略！");
            return;
        }

        // 创建 TesterStepHandler
        int ctype = jsonObject.getInteger("ctype"); // catalogType 分类类型 - 上报日志类型
        int cid = jsonObject.getInteger("cid"); // case id
        String ic = jsonObject.getString("ic"); // iteration count
        TesterStepHandler testerStepHandler = new TesterStepHandler(ctype, cid, ic, rid, RunningModeStatus.TESTING, "");

        // 设置全局参数
        JSONObject gp = jsonObject.getJSONObject("gp");
        testerStepHandler.setGlobalParams(gp);

        // 将 DataProvider 传进来的数据，设置到全局参数中
        JSONObject testData = jsonObject.getJSONObject("testData");
        testerStepHandler.setGlobalParams(testData);

        // 启动任务
        TesterTaskBootThread bootThread = new TesterTaskBootThread(jsonObject, testerStepHandler);
        if (!runningTestsMap.containsKey(rid + "")) {
            logger.info("任务【{}】中断，跳过", bootThread.getName());
            return;
        }
        TaskManager.startBootThread(bootThread);

        // TODO: 2023/4/26 后续这里改成多线程并发的
        // 保证用例串行，等待这个用例运行完成
        try {
            bootThread.waitFinished();
        } catch (InterruptedException e) {
            e.printStackTrace();
        }

        // 强制停止，则退出
        if (bootThread.getForceStop()) {
            logger.info("任务【{}】中断，跳过", bootThread.getName());
            return;
        }
        logger.info("任务【{}】完成", bootThread.getName());
    }

    /**
     * 将 dataProvider 的 json 对象，转换成 list 数组
     * dataProvider 的 json 对象，是一个二维数组，第一行是表头，后面的行是数据。例如：[{"B":"id","C":"name"},{"B":"2"},{"C":"xxx"},{"C":"name2"},{}]
     * @param dataProvider dataProvider 的 json 对象
     * @return
     */
    private List<Map<String, String>> convertDataProvider(JSONArray dataProvider) {
        List<Map<String, String>> dpList = new ArrayList<>();

        if (dataProvider != null) {
            JSONObject headerJson = (JSONObject) dataProvider.get(0); // 得到第一行，作为表头
            for (int i = 1; i < dataProvider.size(); i++) {
                JSONObject dataJson = (JSONObject) dataProvider.get(i); // 得到数据行
                if (dataJson.size() > 0) { // 如果数据行不为空
                    Map<String, String> dataMap = new HashMap<>(); // 用于存放当前数据行的数据

                    headerJson.keySet().forEach(key -> { // 遍历表头
                        String dataKey = headerJson.getString(key); // 从表头中得到字段名
                        if (StringUtils.isNotBlank(dataKey)) { // 如果字段名不为空
                            String dataValue = dataJson.getString(key); // 从数据行中得到字段值
                            if (dataValue == null) { // 如果字段值为空，将其置为空字符串
                                dataValue = "";
                            }
                            dataMap.put(dataKey, dataValue);
                        }
                    });

                    if (dataMap.size() > 0) {
                        dpList.add(dataMap);
                    }
                }
            }
        }

        return dpList;
    }
}
